package utils

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"
)

// 从函数名中截取出包名 格式为:<package-path>[.<receiverName>].<funcName>
func PackageFromFunction(function string) string {
	for {
		indexDot := strings.LastIndex(function, ".")
		indexSlash := strings.LastIndex(function, "/")
		if indexDot > indexSlash {
			function = function[:indexDot]
		} else {
			break
		}
	}

	return function
}

// 根据调用帧获取以包为基准的文件位置以及函数名
func CallerStringFuncFile(frame *runtime.Frame) (string, string) {
	pkgName := PackageFromFunction(frame.Function)
	indexSlash := strings.LastIndex(frame.File, "/")
	fileStr := fmt.Sprintf("%s/%s:%d", pkgName, frame.File[indexSlash+1:], frame.Line)
	return frame.Function[len(pkgName)+1:], fileStr
}

// 获取调用帧 跳过指定层数
func CallerFrame(skip int) *runtime.Frame {
	frames := make([]uintptr, 1)
	runtime.Callers(skip+2, frames)
	frame, _ := runtime.CallersFrames(frames[:]).Next()
	return &frame
}

// 获取调用栈 跳过指定层数
func StackTraceback(skip int, buffer *bytes.Buffer) {
	pcs := make([]uintptr, 32)

	numFrames := 0
	for {
		numFrames = runtime.Callers(skip+2, pcs)
		if numFrames < len(pcs) {
			break
		}

		pcs = make([]uintptr, len(pcs)*2)
	}

	buffer.WriteString("Stack Traceback:\n")
	frames := runtime.CallersFrames(pcs[:numFrames])
	for frame, more := frames.Next(); more; frame, more = frames.Next() {
		funcStr, fileStr := CallerStringFuncFile(&frame)
		fmt.Fprintf(buffer, "%s()\n\t%s\n", funcStr, fileStr)
	}
}

// 获取调用栈 跳过指定层数
func StackTracebackBuf(skip int) (buffer *bytes.Buffer) {
	buffer = new(bytes.Buffer)
	StackTraceback(skip+1, buffer)
	return
}
